Skip to content

Conversation

@fhahn
Copy link
Contributor

@fhahn fhahn commented Sep 11, 2025

Add initial support for PtrToAddr to SCEV, including a new SCEVPtrToAddrExpr and SCEV expansion support for it.

@llvmbot llvmbot added backend:PowerPC llvm:ir llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Sep 11, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 11, 2025

@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-backend-powerpc

Author: Florian Hahn (fhahn)

Changes

Add support for PtrToAddr to SCEV and use it to compute pointer difference when computing backedge taken counts.

This patch retains SCEVPtrToInt, which is mostly used to expressions comping directly from IR.

PtrToAddr more closely matches the semantics of most uses in SCEV. See #156978 for some related discussion.

The patch still retains SCEVPtrToInt, as it is used for cases where a SCEV expression is used in IR by a ptrtoint, e.g. https://github.com/llvm/llvm-project/blob/main/llvm/test/Transforms/IndVarSimplify/pr59633.ll. Some (or perhaps all, still need to check) should probably just be SCEVUnknown.


Patch is 124.87 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/158032.diff

47 Files Affected:

  • (modified) llvm/include/llvm/Analysis/ScalarEvolution.h (+7-2)
  • (modified) llvm/include/llvm/Analysis/ScalarEvolutionDivision.h (+1)
  • (modified) llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h (+26-2)
  • (modified) llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h (+2)
  • (modified) llvm/lib/Analysis/ScalarEvolution.cpp (+98-35)
  • (modified) llvm/lib/IR/Instructions.cpp (+4)
  • (modified) llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp (+11)
  • (modified) llvm/test/Analysis/LoopAccessAnalysis/runtime-check-known-true.ll (+3-3)
  • (modified) llvm/test/Analysis/ScalarEvolution/flags-from-poison.ll (+16-16)
  • (modified) llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll (+20-20)
  • (modified) llvm/test/Analysis/ScalarEvolution/no-wrap-symbolic-becount.ll (+4-4)
  • (modified) llvm/test/Analysis/ScalarEvolution/nsw.ll (+14-14)
  • (modified) llvm/test/Analysis/ScalarEvolution/predicated-max-backedge-taken-count-guard-info.ll (+19-19)
  • (modified) llvm/test/Transforms/IndVarSimplify/2011-11-01-lftrptr.ll (+6-6)
  • (modified) llvm/test/Transforms/IndVarSimplify/lftr.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopStrengthReduce/X86/expander-crashes.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopUnroll/AArch64/apple-unrolling-multi-exit.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopUnroll/AArch64/unrolling-multi-exit.ll (+8-8)
  • (modified) llvm/test/Transforms/LoopVectorize/AArch64/epilog-vectorization-factors.ll (+11-7)
  • (modified) llvm/test/Transforms/LoopVectorize/AArch64/induction-costs.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/AArch64/sve-live-out-pointer-induction.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/AArch64/transform-narrow-interleave-to-widen-memory-derived-ivs.ll (+16-16)
  • (modified) llvm/test/Transforms/LoopVectorize/PowerPC/exit-branch-cost.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/PowerPC/pr41179.ll (+3-3)
  • (modified) llvm/test/Transforms/LoopVectorize/WebAssembly/induction-branch-cost.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/cost-model.ll (+3-3)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/interleave-opaque-pointers.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/optsize.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/pr48340.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/pr72969.ll (+3-3)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/predicate-switch.ll (+26-26)
  • (modified) llvm/test/Transforms/LoopVectorize/epilog-vectorization-any-of-reductions.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/interleaved-accesses-different-insert-position.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/interleaved-accesses-metadata.ll (+6-6)
  • (modified) llvm/test/Transforms/LoopVectorize/opaque-ptr.ll (+12-12)
  • (modified) llvm/test/Transforms/LoopVectorize/pointer-induction.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopVectorize/pr45259.ll (+3-3)
  • (modified) llvm/test/Transforms/LoopVectorize/predicate-switch.ll (+14-14)
  • (modified) llvm/test/Transforms/LoopVectorize/preserve-dbg-loc-and-loop-metadata.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopVectorize/runtime-check-known-true.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopVectorize/runtime-check-needed-but-empty.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopVectorize/scev-predicate-reasoning.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopVectorize/vplan-predicate-switch.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/vplan-sink-scalars-and-merge.ll (+1-1)
  • (modified) llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll (+5-5)
  • (modified) llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll (+2-2)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 858c1d5392071..cf6b2ccb0f22a 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -563,8 +563,13 @@ class ScalarEvolution {
   LLVM_ABI const SCEV *getConstant(ConstantInt *V);
   LLVM_ABI const SCEV *getConstant(const APInt &Val);
   LLVM_ABI const SCEV *getConstant(Type *Ty, uint64_t V, bool isSigned = false);
-  LLVM_ABI const SCEV *getLosslessPtrToIntExpr(const SCEV *Op,
-                                               unsigned Depth = 0);
+  LLVM_ABI const SCEV *getLosslessPtrToIntOrAddrExpr(SCEVTypes Kind,
+                                                     const SCEV *Op,
+                                                     unsigned Depth = 0);
+  LLVM_ABI const SCEV *getLosslessPtrToIntExpr(const SCEV *Op);
+  LLVM_ABI const SCEV *getLosslessPtrToAddrExpr(const SCEV *Op);
+
+  LLVM_ABI const SCEV *getPtrToAddrExpr(const SCEV *Op, Type *Ty);
   LLVM_ABI const SCEV *getPtrToIntExpr(const SCEV *Op, Type *Ty);
   LLVM_ABI const SCEV *getTruncateExpr(const SCEV *Op, Type *Ty,
                                        unsigned Depth = 0);
diff --git a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
index 7c78487fea8c0..d3df7e346b4a5 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
@@ -46,6 +46,7 @@ struct SCEVDivision : public SCEVVisitor<SCEVDivision, void> {
 
   // Except in the trivial case described above, we do not know how to divide
   // Expr by Denominator for the following functions with empty implementation.
+  void visitPtrToAddrExpr(const SCEVPtrToAddrExpr *Numerator) {}
   void visitPtrToIntExpr(const SCEVPtrToIntExpr *Numerator) {}
   void visitTruncateExpr(const SCEVTruncateExpr *Numerator) {}
   void visitZeroExtendExpr(const SCEVZeroExtendExpr *Numerator) {}
diff --git a/llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h b/llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h
index 13b9e1b812942..6ae7b1b08773a 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h
@@ -53,6 +53,7 @@ enum SCEVTypes : unsigned short {
   scSMinExpr,
   scSequentialUMinExpr,
   scPtrToInt,
+  scPtrToAddr,
   scUnknown,
   scCouldNotCompute
 };
@@ -121,8 +122,9 @@ class SCEVCastExpr : public SCEV {
 
   /// Methods for support type inquiry through isa, cast, and dyn_cast:
   static bool classof(const SCEV *S) {
-    return S->getSCEVType() == scPtrToInt || S->getSCEVType() == scTruncate ||
-           S->getSCEVType() == scZeroExtend || S->getSCEVType() == scSignExtend;
+    return S->getSCEVType() == scPtrToAddr || S->getSCEVType() == scPtrToInt ||
+           S->getSCEVType() == scTruncate || S->getSCEVType() == scZeroExtend ||
+           S->getSCEVType() == scSignExtend;
   }
 };
 
@@ -138,6 +140,18 @@ class SCEVPtrToIntExpr : public SCEVCastExpr {
   static bool classof(const SCEV *S) { return S->getSCEVType() == scPtrToInt; }
 };
 
+/// This class represents a cast from a pointer to a pointer-sized integer
+/// value, without capturing the provenance of the pointer.
+class SCEVPtrToAddrExpr : public SCEVCastExpr {
+  friend class ScalarEvolution;
+
+  SCEVPtrToAddrExpr(const FoldingSetNodeIDRef ID, const SCEV *Op, Type *ITy);
+
+public:
+  /// Methods for support type inquiry through isa, cast, and dyn_cast:
+  static bool classof(const SCEV *S) { return S->getSCEVType() == scPtrToAddr; }
+};
+
 /// This is the base class for unary integral cast operator classes.
 class SCEVIntegralCastExpr : public SCEVCastExpr {
 protected:
@@ -615,6 +629,8 @@ template <typename SC, typename RetVal = void> struct SCEVVisitor {
       return ((SC *)this)->visitConstant((const SCEVConstant *)S);
     case scVScale:
       return ((SC *)this)->visitVScale((const SCEVVScale *)S);
+    case scPtrToAddr:
+      return ((SC *)this)->visitPtrToAddrExpr((const SCEVPtrToAddrExpr *)S);
     case scPtrToInt:
       return ((SC *)this)->visitPtrToIntExpr((const SCEVPtrToIntExpr *)S);
     case scTruncate:
@@ -685,6 +701,7 @@ template <typename SV> class SCEVTraversal {
       case scVScale:
       case scUnknown:
         continue;
+      case scPtrToAddr:
       case scPtrToInt:
       case scTruncate:
       case scZeroExtend:
@@ -774,6 +791,13 @@ class SCEVRewriteVisitor : public SCEVVisitor<SC, const SCEV *> {
 
   const SCEV *visitVScale(const SCEVVScale *VScale) { return VScale; }
 
+  const SCEV *visitPtrToAddrExpr(const SCEVPtrToAddrExpr *Expr) {
+    const SCEV *Operand = ((SC *)this)->visit(Expr->getOperand());
+    return Operand == Expr->getOperand()
+               ? Expr
+               : SE.getPtrToAddrExpr(Operand, Expr->getType());
+  }
+
   const SCEV *visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
     const SCEV *Operand = ((SC *)this)->visit(Expr->getOperand());
     return Operand == Expr->getOperand()
diff --git a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h b/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h
index 310118078695c..c986f78db5c19 100644
--- a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h
+++ b/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h
@@ -498,6 +498,8 @@ class SCEVExpander : public SCEVVisitor<SCEVExpander, Value *> {
 
   Value *visitVScale(const SCEVVScale *S);
 
+  Value *visitPtrToAddrExpr(const SCEVPtrToAddrExpr *S);
+
   Value *visitPtrToIntExpr(const SCEVPtrToIntExpr *S);
 
   Value *visitTruncateExpr(const SCEVTruncateExpr *S);
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index ebb863076d2c5..ac405ad579d9c 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -277,10 +277,12 @@ void SCEV::print(raw_ostream &OS) const {
   case scVScale:
     OS << "vscale";
     return;
+  case scPtrToAddr:
   case scPtrToInt: {
-    const SCEVPtrToIntExpr *PtrToInt = cast<SCEVPtrToIntExpr>(this);
+    const SCEVCastExpr *PtrToInt = cast<SCEVCastExpr>(this);
     const SCEV *Op = PtrToInt->getOperand();
-    OS << "(ptrtoint " << *Op->getType() << " " << *Op << " to "
+    StringRef OpS = getSCEVType() == scPtrToAddr ? "addr" : "int";
+    OS << "(ptrto" << OpS << " " << *Op->getType() << " " << *Op << " to "
        << *PtrToInt->getType() << ")";
     return;
   }
@@ -386,6 +388,7 @@ Type *SCEV::getType() const {
     return cast<SCEVConstant>(this)->getType();
   case scVScale:
     return cast<SCEVVScale>(this)->getType();
+  case scPtrToAddr:
   case scPtrToInt:
   case scTruncate:
   case scZeroExtend:
@@ -420,6 +423,7 @@ ArrayRef<const SCEV *> SCEV::operands() const {
   case scVScale:
   case scUnknown:
     return {};
+  case scPtrToAddr:
   case scPtrToInt:
   case scTruncate:
   case scZeroExtend:
@@ -512,6 +516,13 @@ SCEVCastExpr::SCEVCastExpr(const FoldingSetNodeIDRef ID, SCEVTypes SCEVTy,
                            const SCEV *op, Type *ty)
     : SCEV(ID, SCEVTy, computeExpressionSize(op)), Op(op), Ty(ty) {}
 
+SCEVPtrToAddrExpr::SCEVPtrToAddrExpr(const FoldingSetNodeIDRef ID,
+                                     const SCEV *Op, Type *ITy)
+    : SCEVCastExpr(ID, scPtrToAddr, Op, ITy) {
+  assert(getOperand()->getType()->isPointerTy() && Ty->isIntegerTy() &&
+         "Must be a non-bit-width-changing pointer-to-integer cast!");
+}
+
 SCEVPtrToIntExpr::SCEVPtrToIntExpr(const FoldingSetNodeIDRef ID, const SCEV *Op,
                                    Type *ITy)
     : SCEVCastExpr(ID, scPtrToInt, Op, ITy) {
@@ -724,6 +735,7 @@ CompareSCEVComplexity(const LoopInfo *const LI, const SCEV *LHS,
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
@@ -1004,10 +1016,11 @@ SCEVAddRecExpr::evaluateAtIteration(ArrayRef<const SCEV *> Operands,
 //                    SCEV Expression folder implementations
 //===----------------------------------------------------------------------===//
 
-const SCEV *ScalarEvolution::getLosslessPtrToIntExpr(const SCEV *Op,
-                                                     unsigned Depth) {
+const SCEV *ScalarEvolution::getLosslessPtrToIntOrAddrExpr(SCEVTypes Kind,
+                                                           const SCEV *Op,
+                                                           unsigned Depth) {
   assert(Depth <= 1 &&
-         "getLosslessPtrToIntExpr() should self-recurse at most once.");
+         "getLosslessPtrToIntOrAddrExpr() should self-recurse at most once.");
 
   // We could be called with an integer-typed operands during SCEV rewrites.
   // Since the operand is an integer already, just perform zext/trunc/self cast.
@@ -1052,35 +1065,45 @@ const SCEV *ScalarEvolution::getLosslessPtrToIntExpr(const SCEV *Op,
     // Create an explicit cast node.
     // We can reuse the existing insert position since if we get here,
     // we won't have made any changes which would invalidate it.
-    SCEV *S = new (SCEVAllocator)
-        SCEVPtrToIntExpr(ID.Intern(SCEVAllocator), Op, IntPtrTy);
+    SCEV *S;
+    if (Kind == scPtrToInt) {
+      S = new (SCEVAllocator)
+          SCEVPtrToIntExpr(ID.Intern(SCEVAllocator), Op, IntPtrTy);
+    } else {
+      S = new (SCEVAllocator)
+          SCEVPtrToAddrExpr(ID.Intern(SCEVAllocator), Op, IntPtrTy);
+    }
     UniqueSCEVs.InsertNode(S, IP);
     registerUser(S, Op);
     return S;
   }
 
-  assert(Depth == 0 && "getLosslessPtrToIntExpr() should not self-recurse for "
-                       "non-SCEVUnknown's.");
+  assert(Depth == 0 &&
+         "getLosslessPtrToIntOrAddrExpr() should not self-recurse for "
+         "non-SCEVUnknown's.");
 
   // Otherwise, we've got some expression that is more complex than just a
-  // single SCEVUnknown. But we don't want to have a SCEVPtrToIntExpr of an
-  // arbitrary expression, we want to have SCEVPtrToIntExpr of an SCEVUnknown
-  // only, and the expressions must otherwise be integer-typed.
-  // So sink the cast down to the SCEVUnknown's.
+  // single SCEVUnknown. But we don't want to have a SCEVPtrTo(Int|Addr)Expr of
+  // an arbitrary expression, we want to have SCEVPtrTo(Int|Addr)Expr of an
+  // SCEVUnknown only, and the expressions must otherwise be integer-typed. So
+  // sink the cast down to the SCEVUnknown's.
 
-  /// The SCEVPtrToIntSinkingRewriter takes a scalar evolution expression,
+  /// The SCEVPtrToIntOrAddrSinkingRewriter takes a scalar evolution expression,
   /// which computes a pointer-typed value, and rewrites the whole expression
   /// tree so that *all* the computations are done on integers, and the only
   /// pointer-typed operands in the expression are SCEVUnknown.
-  class SCEVPtrToIntSinkingRewriter
-      : public SCEVRewriteVisitor<SCEVPtrToIntSinkingRewriter> {
-    using Base = SCEVRewriteVisitor<SCEVPtrToIntSinkingRewriter>;
+  class SCEVPtrToIntOrAddrSinkingRewriter
+      : public SCEVRewriteVisitor<SCEVPtrToIntOrAddrSinkingRewriter> {
+    using Base = SCEVRewriteVisitor<SCEVPtrToIntOrAddrSinkingRewriter>;
+    const SCEVTypes Kind;
 
   public:
-    SCEVPtrToIntSinkingRewriter(ScalarEvolution &SE) : SCEVRewriteVisitor(SE) {}
+    SCEVPtrToIntOrAddrSinkingRewriter(SCEVTypes Kind, ScalarEvolution &SE)
+        : SCEVRewriteVisitor(SE), Kind(Kind) {}
 
-    static const SCEV *rewrite(const SCEV *Scev, ScalarEvolution &SE) {
-      SCEVPtrToIntSinkingRewriter Rewriter(SE);
+    static const SCEV *rewrite(const SCEV *Scev, SCEVTypes Kind,
+                               ScalarEvolution &SE) {
+      SCEVPtrToIntOrAddrSinkingRewriter Rewriter(Kind, SE);
       return Rewriter.visit(Scev);
     }
 
@@ -1116,18 +1139,37 @@ const SCEV *ScalarEvolution::getLosslessPtrToIntExpr(const SCEV *Op,
     const SCEV *visitUnknown(const SCEVUnknown *Expr) {
       assert(Expr->getType()->isPointerTy() &&
              "Should only reach pointer-typed SCEVUnknown's.");
-      return SE.getLosslessPtrToIntExpr(Expr, /*Depth=*/1);
+      return SE.getLosslessPtrToIntOrAddrExpr(Kind, Expr, /*Depth=*/1);
     }
   };
 
   // And actually perform the cast sinking.
-  const SCEV *IntOp = SCEVPtrToIntSinkingRewriter::rewrite(Op, *this);
+  const SCEV *IntOp =
+      SCEVPtrToIntOrAddrSinkingRewriter::rewrite(Op, Kind, *this);
   assert(IntOp->getType()->isIntegerTy() &&
          "We must have succeeded in sinking the cast, "
          "and ending up with an integer-typed expression!");
   return IntOp;
 }
 
+const SCEV *ScalarEvolution::getLosslessPtrToAddrExpr(const SCEV *Op) {
+  return getLosslessPtrToIntOrAddrExpr(scPtrToAddr, Op);
+}
+
+const SCEV *ScalarEvolution::getLosslessPtrToIntExpr(const SCEV *Op) {
+  return getLosslessPtrToIntOrAddrExpr(scPtrToInt, Op);
+}
+
+const SCEV *ScalarEvolution::getPtrToAddrExpr(const SCEV *Op, Type *Ty) {
+  assert(Ty->isIntegerTy() && "Target type must be an integer type!");
+
+  const SCEV *IntOp = getLosslessPtrToAddrExpr(Op);
+  if (isa<SCEVCouldNotCompute>(IntOp))
+    return IntOp;
+
+  return getTruncateOrZeroExtend(IntOp, Ty);
+}
+
 const SCEV *ScalarEvolution::getPtrToIntExpr(const SCEV *Op, Type *Ty) {
   assert(Ty->isIntegerTy() && "Target type must be an integer type!");
 
@@ -4073,6 +4115,8 @@ class SCEVSequentialMinMaxDeduplicatingVisitor final
 
   RetVal visitVScale(const SCEVVScale *VScale) { return VScale; }
 
+  RetVal visitPtrToAddrExpr(const SCEVPtrToAddrExpr *Expr) { return Expr; }
+
   RetVal visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) { return Expr; }
 
   RetVal visitTruncateExpr(const SCEVTruncateExpr *Expr) { return Expr; }
@@ -4123,6 +4167,7 @@ static bool scevUnconditionallyPropagatesPoisonFromOperands(SCEVTypes Kind) {
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
@@ -6362,8 +6407,9 @@ APInt ScalarEvolution::getConstantMultipleImpl(const SCEV *S) {
   switch (S->getSCEVType()) {
   case scConstant:
     return cast<SCEVConstant>(S)->getAPInt();
+  case scPtrToAddr:
   case scPtrToInt:
-    return getConstantMultiple(cast<SCEVPtrToIntExpr>(S)->getOperand());
+    return getConstantMultiple(cast<SCEVCastExpr>(S)->getOperand());
   case scUDivExpr:
   case scVScale:
     return APInt(BitWidth, 1);
@@ -6623,6 +6669,7 @@ ScalarEvolution::getRangeRefIter(const SCEV *S,
     case scTruncate:
     case scZeroExtend:
     case scSignExtend:
+    case scPtrToAddr:
     case scPtrToInt:
     case scAddExpr:
     case scMulExpr:
@@ -6750,10 +6797,11 @@ const ConstantRange &ScalarEvolution::getRangeRef(
         SExt, SignHint,
         ConservativeResult.intersectWith(X.signExtend(BitWidth), RangeType));
   }
+  case scPtrToAddr:
   case scPtrToInt: {
-    const SCEVPtrToIntExpr *PtrToInt = cast<SCEVPtrToIntExpr>(S);
-    ConstantRange X = getRangeRef(PtrToInt->getOperand(), SignHint, Depth + 1);
-    return setRange(PtrToInt, SignHint, X);
+    const SCEVCastExpr *Cast = cast<SCEVCastExpr>(S);
+    ConstantRange X = getRangeRef(Cast->getOperand(), SignHint, Depth + 1);
+    return setRange(Cast, SignHint, X);
   }
   case scAddExpr: {
     const SCEVAddExpr *Add = cast<SCEVAddExpr>(S);
@@ -7646,6 +7694,7 @@ ScalarEvolution::getOperandsToCreate(Value *V, SmallVectorImpl<Value *> &Ops) {
   case Instruction::Trunc:
   case Instruction::ZExt:
   case Instruction::SExt:
+  case Instruction::PtrToAddr:
   case Instruction::PtrToInt:
     Ops.push_back(U->getOperand(0));
     return nullptr;
@@ -8118,13 +8167,16 @@ const SCEV *ScalarEvolution::createSCEV(Value *V) {
       return getSCEV(U->getOperand(0));
     break;
 
+  case Instruction::PtrToAddr:
   case Instruction::PtrToInt: {
     // Pointer to integer cast is straight-forward, so do model it.
     const SCEV *Op = getSCEV(U->getOperand(0));
     Type *DstIntTy = U->getType();
     // But only if effective SCEV (integer) type is wide enough to represent
     // all possible pointer values.
-    const SCEV *IntOp = getPtrToIntExpr(Op, DstIntTy);
+    const SCEV *IntOp = U->getOpcode() == Instruction::PtrToInt
+                            ? getPtrToIntExpr(Op, DstIntTy)
+                            : getPtrToAddrExpr(Op, DstIntTy);
     if (isa<SCEVCouldNotCompute>(IntOp))
       return getUnknown(V);
     return IntOp;
@@ -9313,12 +9365,12 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
   case ICmpInst::ICMP_NE: {                     // while (X != Y)
     // Convert to: while (X-Y != 0)
     if (LHS->getType()->isPointerTy()) {
-      LHS = getLosslessPtrToIntExpr(LHS);
+      LHS = getLosslessPtrToAddrExpr(LHS);
       if (isa<SCEVCouldNotCompute>(LHS))
         return LHS;
     }
     if (RHS->getType()->isPointerTy()) {
-      RHS = getLosslessPtrToIntExpr(RHS);
+      RHS = getLosslessPtrToAddrExpr(RHS);
       if (isa<SCEVCouldNotCompute>(RHS))
         return RHS;
     }
@@ -9331,12 +9383,12 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
   case ICmpInst::ICMP_EQ: {                     // while (X == Y)
     // Convert to: while (X-Y == 0)
     if (LHS->getType()->isPointerTy()) {
-      LHS = getLosslessPtrToIntExpr(LHS);
+      LHS = getLosslessPtrToAddrExpr(LHS);
       if (isa<SCEVCouldNotCompute>(LHS))
         return LHS;
     }
     if (RHS->getType()->isPointerTy()) {
-      RHS = getLosslessPtrToIntExpr(RHS);
+      RHS = getLosslessPtrToAddrExpr(RHS);
       if (isa<SCEVCouldNotCompute>(RHS))
         return RHS;
     }
@@ -9926,6 +9978,13 @@ static Constant *BuildConstantFromSCEV(const SCEV *V) {
     return cast<SCEVConstant>(V)->getValue();
   case scUnknown:
     return dyn_cast<Constant>(cast<SCEVUnknown>(V)->getValue());
+  case scPtrToAddr: {
+    const SCEVPtrToAddrExpr *P2I = cast<SCEVPtrToAddrExpr>(V);
+    if (Constant *CastOp = BuildConstantFromSCEV(P2I->getOperand()))
+      return ConstantExpr::getPtrToAddr(CastOp, P2I->getType());
+
+    return nullptr;
+  }
   case scPtrToInt: {
     const SCEVPtrToIntExpr *P2I = cast<SCEVPtrToIntExpr>(V);
     if (Constant *CastOp = BuildConstantFromSCEV(P2I->getOperand()))
@@ -9984,6 +10043,7 @@ ScalarEvolution::getWithOperands(const SCEV *S,
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
     return getCastExpr(S->getSCEVType(), NewOps[0], S->getType());
   case scAddRecExpr: {
@@ -10068,6 +10128,7 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) {
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
@@ -13204,12 +13265,12 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
   const SCEV *OrigStart = Start;
   const SCEV *OrigRHS = RHS;
   if (Start->getType()->isPointerTy()) {
-    Start = getLosslessPtrToIntExpr(Start);
+    Start = getLosslessPtrToAddrExpr(Start);
     if (isa<SCEVCouldNotCompute>(Start))
       return Start;
   }
   if (RHS->getType()->isPointerTy()) {
-    RHS = getLosslessPtrToIntExpr(RHS);
+    RHS = getLosslessPtrToAddrExpr(RHS);
     if (isa<SCEVCouldNotCompute>(RHS))
       return RHS;
   }
@@ -13512,12 +13573,12 @@ ScalarEvolution::ExitLimit ScalarEvolution::howManyGreaterThans(
   }
 
   if (Start->getType()->isPointerTy()) {
-    Start = getLosslessPtrToIntExpr(Start);
+    Start = getLosslessPtrToAddrExpr(Start);
     if (isa<SCEVCouldNotCompute>(Start))
       return Start;
   }
   if (End->getType()->isPointerTy()) {
-    End = getLosslessPtrToIntExpr(End);
+    End = getLosslessPtrToAddrExpr(End);
     if (isa<SCEVCouldNotCompute>(End))
       return End;
   }
@@ -14148,6 +14209,7 @@ ScalarEvolution::computeLoopDisposition(const SCEV *S, const Loop *L) {
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
@@ -14229,6 +14291,7 @@ ScalarEvolution::computeBlockDisposition(const SCEV *S, const BasicBlock *BB) {
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index a1751c0ee3e48..aac4a96704162 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -2894,6 +2894...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Sep 11, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Florian Hahn (fhahn)

Changes

Add support for PtrToAddr to SCEV and use it to compute pointer difference when computing backedge taken counts.

This patch retains SCEVPtrToInt, which is mostly used to expressions comping directly from IR.

PtrToAddr more closely matches the semantics of most uses in SCEV. See #156978 for some related discussion.

The patch still retains SCEVPtrToInt, as it is used for cases where a SCEV expression is used in IR by a ptrtoint, e.g. https://github.com/llvm/llvm-project/blob/main/llvm/test/Transforms/IndVarSimplify/pr59633.ll. Some (or perhaps all, still need to check) should probably just be SCEVUnknown.


Patch is 124.87 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/158032.diff

47 Files Affected:

  • (modified) llvm/include/llvm/Analysis/ScalarEvolution.h (+7-2)
  • (modified) llvm/include/llvm/Analysis/ScalarEvolutionDivision.h (+1)
  • (modified) llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h (+26-2)
  • (modified) llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h (+2)
  • (modified) llvm/lib/Analysis/ScalarEvolution.cpp (+98-35)
  • (modified) llvm/lib/IR/Instructions.cpp (+4)
  • (modified) llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp (+11)
  • (modified) llvm/test/Analysis/LoopAccessAnalysis/runtime-check-known-true.ll (+3-3)
  • (modified) llvm/test/Analysis/ScalarEvolution/flags-from-poison.ll (+16-16)
  • (modified) llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll (+20-20)
  • (modified) llvm/test/Analysis/ScalarEvolution/no-wrap-symbolic-becount.ll (+4-4)
  • (modified) llvm/test/Analysis/ScalarEvolution/nsw.ll (+14-14)
  • (modified) llvm/test/Analysis/ScalarEvolution/predicated-max-backedge-taken-count-guard-info.ll (+19-19)
  • (modified) llvm/test/Transforms/IndVarSimplify/2011-11-01-lftrptr.ll (+6-6)
  • (modified) llvm/test/Transforms/IndVarSimplify/lftr.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopStrengthReduce/X86/expander-crashes.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopUnroll/AArch64/apple-unrolling-multi-exit.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopUnroll/AArch64/unrolling-multi-exit.ll (+8-8)
  • (modified) llvm/test/Transforms/LoopVectorize/AArch64/epilog-vectorization-factors.ll (+11-7)
  • (modified) llvm/test/Transforms/LoopVectorize/AArch64/induction-costs.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/AArch64/sve-live-out-pointer-induction.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/AArch64/transform-narrow-interleave-to-widen-memory-derived-ivs.ll (+16-16)
  • (modified) llvm/test/Transforms/LoopVectorize/PowerPC/exit-branch-cost.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/PowerPC/pr41179.ll (+3-3)
  • (modified) llvm/test/Transforms/LoopVectorize/WebAssembly/induction-branch-cost.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/cost-model.ll (+3-3)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/interleave-opaque-pointers.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/optsize.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/pr48340.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/pr72969.ll (+3-3)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/predicate-switch.ll (+26-26)
  • (modified) llvm/test/Transforms/LoopVectorize/epilog-vectorization-any-of-reductions.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/interleaved-accesses-different-insert-position.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/interleaved-accesses-metadata.ll (+6-6)
  • (modified) llvm/test/Transforms/LoopVectorize/opaque-ptr.ll (+12-12)
  • (modified) llvm/test/Transforms/LoopVectorize/pointer-induction.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopVectorize/pr45259.ll (+3-3)
  • (modified) llvm/test/Transforms/LoopVectorize/predicate-switch.ll (+14-14)
  • (modified) llvm/test/Transforms/LoopVectorize/preserve-dbg-loc-and-loop-metadata.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopVectorize/runtime-check-known-true.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopVectorize/runtime-check-needed-but-empty.ll (+1-1)
  • (modified) llvm/test/Transforms/LoopVectorize/scev-predicate-reasoning.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopVectorize/vplan-predicate-switch.ll (+2-2)
  • (modified) llvm/test/Transforms/LoopVectorize/vplan-sink-scalars-and-merge.ll (+1-1)
  • (modified) llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll (+5-5)
  • (modified) llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll (+2-2)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 858c1d5392071..cf6b2ccb0f22a 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -563,8 +563,13 @@ class ScalarEvolution {
   LLVM_ABI const SCEV *getConstant(ConstantInt *V);
   LLVM_ABI const SCEV *getConstant(const APInt &Val);
   LLVM_ABI const SCEV *getConstant(Type *Ty, uint64_t V, bool isSigned = false);
-  LLVM_ABI const SCEV *getLosslessPtrToIntExpr(const SCEV *Op,
-                                               unsigned Depth = 0);
+  LLVM_ABI const SCEV *getLosslessPtrToIntOrAddrExpr(SCEVTypes Kind,
+                                                     const SCEV *Op,
+                                                     unsigned Depth = 0);
+  LLVM_ABI const SCEV *getLosslessPtrToIntExpr(const SCEV *Op);
+  LLVM_ABI const SCEV *getLosslessPtrToAddrExpr(const SCEV *Op);
+
+  LLVM_ABI const SCEV *getPtrToAddrExpr(const SCEV *Op, Type *Ty);
   LLVM_ABI const SCEV *getPtrToIntExpr(const SCEV *Op, Type *Ty);
   LLVM_ABI const SCEV *getTruncateExpr(const SCEV *Op, Type *Ty,
                                        unsigned Depth = 0);
diff --git a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
index 7c78487fea8c0..d3df7e346b4a5 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
@@ -46,6 +46,7 @@ struct SCEVDivision : public SCEVVisitor<SCEVDivision, void> {
 
   // Except in the trivial case described above, we do not know how to divide
   // Expr by Denominator for the following functions with empty implementation.
+  void visitPtrToAddrExpr(const SCEVPtrToAddrExpr *Numerator) {}
   void visitPtrToIntExpr(const SCEVPtrToIntExpr *Numerator) {}
   void visitTruncateExpr(const SCEVTruncateExpr *Numerator) {}
   void visitZeroExtendExpr(const SCEVZeroExtendExpr *Numerator) {}
diff --git a/llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h b/llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h
index 13b9e1b812942..6ae7b1b08773a 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h
@@ -53,6 +53,7 @@ enum SCEVTypes : unsigned short {
   scSMinExpr,
   scSequentialUMinExpr,
   scPtrToInt,
+  scPtrToAddr,
   scUnknown,
   scCouldNotCompute
 };
@@ -121,8 +122,9 @@ class SCEVCastExpr : public SCEV {
 
   /// Methods for support type inquiry through isa, cast, and dyn_cast:
   static bool classof(const SCEV *S) {
-    return S->getSCEVType() == scPtrToInt || S->getSCEVType() == scTruncate ||
-           S->getSCEVType() == scZeroExtend || S->getSCEVType() == scSignExtend;
+    return S->getSCEVType() == scPtrToAddr || S->getSCEVType() == scPtrToInt ||
+           S->getSCEVType() == scTruncate || S->getSCEVType() == scZeroExtend ||
+           S->getSCEVType() == scSignExtend;
   }
 };
 
@@ -138,6 +140,18 @@ class SCEVPtrToIntExpr : public SCEVCastExpr {
   static bool classof(const SCEV *S) { return S->getSCEVType() == scPtrToInt; }
 };
 
+/// This class represents a cast from a pointer to a pointer-sized integer
+/// value, without capturing the provenance of the pointer.
+class SCEVPtrToAddrExpr : public SCEVCastExpr {
+  friend class ScalarEvolution;
+
+  SCEVPtrToAddrExpr(const FoldingSetNodeIDRef ID, const SCEV *Op, Type *ITy);
+
+public:
+  /// Methods for support type inquiry through isa, cast, and dyn_cast:
+  static bool classof(const SCEV *S) { return S->getSCEVType() == scPtrToAddr; }
+};
+
 /// This is the base class for unary integral cast operator classes.
 class SCEVIntegralCastExpr : public SCEVCastExpr {
 protected:
@@ -615,6 +629,8 @@ template <typename SC, typename RetVal = void> struct SCEVVisitor {
       return ((SC *)this)->visitConstant((const SCEVConstant *)S);
     case scVScale:
       return ((SC *)this)->visitVScale((const SCEVVScale *)S);
+    case scPtrToAddr:
+      return ((SC *)this)->visitPtrToAddrExpr((const SCEVPtrToAddrExpr *)S);
     case scPtrToInt:
       return ((SC *)this)->visitPtrToIntExpr((const SCEVPtrToIntExpr *)S);
     case scTruncate:
@@ -685,6 +701,7 @@ template <typename SV> class SCEVTraversal {
       case scVScale:
       case scUnknown:
         continue;
+      case scPtrToAddr:
       case scPtrToInt:
       case scTruncate:
       case scZeroExtend:
@@ -774,6 +791,13 @@ class SCEVRewriteVisitor : public SCEVVisitor<SC, const SCEV *> {
 
   const SCEV *visitVScale(const SCEVVScale *VScale) { return VScale; }
 
+  const SCEV *visitPtrToAddrExpr(const SCEVPtrToAddrExpr *Expr) {
+    const SCEV *Operand = ((SC *)this)->visit(Expr->getOperand());
+    return Operand == Expr->getOperand()
+               ? Expr
+               : SE.getPtrToAddrExpr(Operand, Expr->getType());
+  }
+
   const SCEV *visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
     const SCEV *Operand = ((SC *)this)->visit(Expr->getOperand());
     return Operand == Expr->getOperand()
diff --git a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h b/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h
index 310118078695c..c986f78db5c19 100644
--- a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h
+++ b/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h
@@ -498,6 +498,8 @@ class SCEVExpander : public SCEVVisitor<SCEVExpander, Value *> {
 
   Value *visitVScale(const SCEVVScale *S);
 
+  Value *visitPtrToAddrExpr(const SCEVPtrToAddrExpr *S);
+
   Value *visitPtrToIntExpr(const SCEVPtrToIntExpr *S);
 
   Value *visitTruncateExpr(const SCEVTruncateExpr *S);
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index ebb863076d2c5..ac405ad579d9c 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -277,10 +277,12 @@ void SCEV::print(raw_ostream &OS) const {
   case scVScale:
     OS << "vscale";
     return;
+  case scPtrToAddr:
   case scPtrToInt: {
-    const SCEVPtrToIntExpr *PtrToInt = cast<SCEVPtrToIntExpr>(this);
+    const SCEVCastExpr *PtrToInt = cast<SCEVCastExpr>(this);
     const SCEV *Op = PtrToInt->getOperand();
-    OS << "(ptrtoint " << *Op->getType() << " " << *Op << " to "
+    StringRef OpS = getSCEVType() == scPtrToAddr ? "addr" : "int";
+    OS << "(ptrto" << OpS << " " << *Op->getType() << " " << *Op << " to "
        << *PtrToInt->getType() << ")";
     return;
   }
@@ -386,6 +388,7 @@ Type *SCEV::getType() const {
     return cast<SCEVConstant>(this)->getType();
   case scVScale:
     return cast<SCEVVScale>(this)->getType();
+  case scPtrToAddr:
   case scPtrToInt:
   case scTruncate:
   case scZeroExtend:
@@ -420,6 +423,7 @@ ArrayRef<const SCEV *> SCEV::operands() const {
   case scVScale:
   case scUnknown:
     return {};
+  case scPtrToAddr:
   case scPtrToInt:
   case scTruncate:
   case scZeroExtend:
@@ -512,6 +516,13 @@ SCEVCastExpr::SCEVCastExpr(const FoldingSetNodeIDRef ID, SCEVTypes SCEVTy,
                            const SCEV *op, Type *ty)
     : SCEV(ID, SCEVTy, computeExpressionSize(op)), Op(op), Ty(ty) {}
 
+SCEVPtrToAddrExpr::SCEVPtrToAddrExpr(const FoldingSetNodeIDRef ID,
+                                     const SCEV *Op, Type *ITy)
+    : SCEVCastExpr(ID, scPtrToAddr, Op, ITy) {
+  assert(getOperand()->getType()->isPointerTy() && Ty->isIntegerTy() &&
+         "Must be a non-bit-width-changing pointer-to-integer cast!");
+}
+
 SCEVPtrToIntExpr::SCEVPtrToIntExpr(const FoldingSetNodeIDRef ID, const SCEV *Op,
                                    Type *ITy)
     : SCEVCastExpr(ID, scPtrToInt, Op, ITy) {
@@ -724,6 +735,7 @@ CompareSCEVComplexity(const LoopInfo *const LI, const SCEV *LHS,
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
@@ -1004,10 +1016,11 @@ SCEVAddRecExpr::evaluateAtIteration(ArrayRef<const SCEV *> Operands,
 //                    SCEV Expression folder implementations
 //===----------------------------------------------------------------------===//
 
-const SCEV *ScalarEvolution::getLosslessPtrToIntExpr(const SCEV *Op,
-                                                     unsigned Depth) {
+const SCEV *ScalarEvolution::getLosslessPtrToIntOrAddrExpr(SCEVTypes Kind,
+                                                           const SCEV *Op,
+                                                           unsigned Depth) {
   assert(Depth <= 1 &&
-         "getLosslessPtrToIntExpr() should self-recurse at most once.");
+         "getLosslessPtrToIntOrAddrExpr() should self-recurse at most once.");
 
   // We could be called with an integer-typed operands during SCEV rewrites.
   // Since the operand is an integer already, just perform zext/trunc/self cast.
@@ -1052,35 +1065,45 @@ const SCEV *ScalarEvolution::getLosslessPtrToIntExpr(const SCEV *Op,
     // Create an explicit cast node.
     // We can reuse the existing insert position since if we get here,
     // we won't have made any changes which would invalidate it.
-    SCEV *S = new (SCEVAllocator)
-        SCEVPtrToIntExpr(ID.Intern(SCEVAllocator), Op, IntPtrTy);
+    SCEV *S;
+    if (Kind == scPtrToInt) {
+      S = new (SCEVAllocator)
+          SCEVPtrToIntExpr(ID.Intern(SCEVAllocator), Op, IntPtrTy);
+    } else {
+      S = new (SCEVAllocator)
+          SCEVPtrToAddrExpr(ID.Intern(SCEVAllocator), Op, IntPtrTy);
+    }
     UniqueSCEVs.InsertNode(S, IP);
     registerUser(S, Op);
     return S;
   }
 
-  assert(Depth == 0 && "getLosslessPtrToIntExpr() should not self-recurse for "
-                       "non-SCEVUnknown's.");
+  assert(Depth == 0 &&
+         "getLosslessPtrToIntOrAddrExpr() should not self-recurse for "
+         "non-SCEVUnknown's.");
 
   // Otherwise, we've got some expression that is more complex than just a
-  // single SCEVUnknown. But we don't want to have a SCEVPtrToIntExpr of an
-  // arbitrary expression, we want to have SCEVPtrToIntExpr of an SCEVUnknown
-  // only, and the expressions must otherwise be integer-typed.
-  // So sink the cast down to the SCEVUnknown's.
+  // single SCEVUnknown. But we don't want to have a SCEVPtrTo(Int|Addr)Expr of
+  // an arbitrary expression, we want to have SCEVPtrTo(Int|Addr)Expr of an
+  // SCEVUnknown only, and the expressions must otherwise be integer-typed. So
+  // sink the cast down to the SCEVUnknown's.
 
-  /// The SCEVPtrToIntSinkingRewriter takes a scalar evolution expression,
+  /// The SCEVPtrToIntOrAddrSinkingRewriter takes a scalar evolution expression,
   /// which computes a pointer-typed value, and rewrites the whole expression
   /// tree so that *all* the computations are done on integers, and the only
   /// pointer-typed operands in the expression are SCEVUnknown.
-  class SCEVPtrToIntSinkingRewriter
-      : public SCEVRewriteVisitor<SCEVPtrToIntSinkingRewriter> {
-    using Base = SCEVRewriteVisitor<SCEVPtrToIntSinkingRewriter>;
+  class SCEVPtrToIntOrAddrSinkingRewriter
+      : public SCEVRewriteVisitor<SCEVPtrToIntOrAddrSinkingRewriter> {
+    using Base = SCEVRewriteVisitor<SCEVPtrToIntOrAddrSinkingRewriter>;
+    const SCEVTypes Kind;
 
   public:
-    SCEVPtrToIntSinkingRewriter(ScalarEvolution &SE) : SCEVRewriteVisitor(SE) {}
+    SCEVPtrToIntOrAddrSinkingRewriter(SCEVTypes Kind, ScalarEvolution &SE)
+        : SCEVRewriteVisitor(SE), Kind(Kind) {}
 
-    static const SCEV *rewrite(const SCEV *Scev, ScalarEvolution &SE) {
-      SCEVPtrToIntSinkingRewriter Rewriter(SE);
+    static const SCEV *rewrite(const SCEV *Scev, SCEVTypes Kind,
+                               ScalarEvolution &SE) {
+      SCEVPtrToIntOrAddrSinkingRewriter Rewriter(Kind, SE);
       return Rewriter.visit(Scev);
     }
 
@@ -1116,18 +1139,37 @@ const SCEV *ScalarEvolution::getLosslessPtrToIntExpr(const SCEV *Op,
     const SCEV *visitUnknown(const SCEVUnknown *Expr) {
       assert(Expr->getType()->isPointerTy() &&
              "Should only reach pointer-typed SCEVUnknown's.");
-      return SE.getLosslessPtrToIntExpr(Expr, /*Depth=*/1);
+      return SE.getLosslessPtrToIntOrAddrExpr(Kind, Expr, /*Depth=*/1);
     }
   };
 
   // And actually perform the cast sinking.
-  const SCEV *IntOp = SCEVPtrToIntSinkingRewriter::rewrite(Op, *this);
+  const SCEV *IntOp =
+      SCEVPtrToIntOrAddrSinkingRewriter::rewrite(Op, Kind, *this);
   assert(IntOp->getType()->isIntegerTy() &&
          "We must have succeeded in sinking the cast, "
          "and ending up with an integer-typed expression!");
   return IntOp;
 }
 
+const SCEV *ScalarEvolution::getLosslessPtrToAddrExpr(const SCEV *Op) {
+  return getLosslessPtrToIntOrAddrExpr(scPtrToAddr, Op);
+}
+
+const SCEV *ScalarEvolution::getLosslessPtrToIntExpr(const SCEV *Op) {
+  return getLosslessPtrToIntOrAddrExpr(scPtrToInt, Op);
+}
+
+const SCEV *ScalarEvolution::getPtrToAddrExpr(const SCEV *Op, Type *Ty) {
+  assert(Ty->isIntegerTy() && "Target type must be an integer type!");
+
+  const SCEV *IntOp = getLosslessPtrToAddrExpr(Op);
+  if (isa<SCEVCouldNotCompute>(IntOp))
+    return IntOp;
+
+  return getTruncateOrZeroExtend(IntOp, Ty);
+}
+
 const SCEV *ScalarEvolution::getPtrToIntExpr(const SCEV *Op, Type *Ty) {
   assert(Ty->isIntegerTy() && "Target type must be an integer type!");
 
@@ -4073,6 +4115,8 @@ class SCEVSequentialMinMaxDeduplicatingVisitor final
 
   RetVal visitVScale(const SCEVVScale *VScale) { return VScale; }
 
+  RetVal visitPtrToAddrExpr(const SCEVPtrToAddrExpr *Expr) { return Expr; }
+
   RetVal visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) { return Expr; }
 
   RetVal visitTruncateExpr(const SCEVTruncateExpr *Expr) { return Expr; }
@@ -4123,6 +4167,7 @@ static bool scevUnconditionallyPropagatesPoisonFromOperands(SCEVTypes Kind) {
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
@@ -6362,8 +6407,9 @@ APInt ScalarEvolution::getConstantMultipleImpl(const SCEV *S) {
   switch (S->getSCEVType()) {
   case scConstant:
     return cast<SCEVConstant>(S)->getAPInt();
+  case scPtrToAddr:
   case scPtrToInt:
-    return getConstantMultiple(cast<SCEVPtrToIntExpr>(S)->getOperand());
+    return getConstantMultiple(cast<SCEVCastExpr>(S)->getOperand());
   case scUDivExpr:
   case scVScale:
     return APInt(BitWidth, 1);
@@ -6623,6 +6669,7 @@ ScalarEvolution::getRangeRefIter(const SCEV *S,
     case scTruncate:
     case scZeroExtend:
     case scSignExtend:
+    case scPtrToAddr:
     case scPtrToInt:
     case scAddExpr:
     case scMulExpr:
@@ -6750,10 +6797,11 @@ const ConstantRange &ScalarEvolution::getRangeRef(
         SExt, SignHint,
         ConservativeResult.intersectWith(X.signExtend(BitWidth), RangeType));
   }
+  case scPtrToAddr:
   case scPtrToInt: {
-    const SCEVPtrToIntExpr *PtrToInt = cast<SCEVPtrToIntExpr>(S);
-    ConstantRange X = getRangeRef(PtrToInt->getOperand(), SignHint, Depth + 1);
-    return setRange(PtrToInt, SignHint, X);
+    const SCEVCastExpr *Cast = cast<SCEVCastExpr>(S);
+    ConstantRange X = getRangeRef(Cast->getOperand(), SignHint, Depth + 1);
+    return setRange(Cast, SignHint, X);
   }
   case scAddExpr: {
     const SCEVAddExpr *Add = cast<SCEVAddExpr>(S);
@@ -7646,6 +7694,7 @@ ScalarEvolution::getOperandsToCreate(Value *V, SmallVectorImpl<Value *> &Ops) {
   case Instruction::Trunc:
   case Instruction::ZExt:
   case Instruction::SExt:
+  case Instruction::PtrToAddr:
   case Instruction::PtrToInt:
     Ops.push_back(U->getOperand(0));
     return nullptr;
@@ -8118,13 +8167,16 @@ const SCEV *ScalarEvolution::createSCEV(Value *V) {
       return getSCEV(U->getOperand(0));
     break;
 
+  case Instruction::PtrToAddr:
   case Instruction::PtrToInt: {
     // Pointer to integer cast is straight-forward, so do model it.
     const SCEV *Op = getSCEV(U->getOperand(0));
     Type *DstIntTy = U->getType();
     // But only if effective SCEV (integer) type is wide enough to represent
     // all possible pointer values.
-    const SCEV *IntOp = getPtrToIntExpr(Op, DstIntTy);
+    const SCEV *IntOp = U->getOpcode() == Instruction::PtrToInt
+                            ? getPtrToIntExpr(Op, DstIntTy)
+                            : getPtrToAddrExpr(Op, DstIntTy);
     if (isa<SCEVCouldNotCompute>(IntOp))
       return getUnknown(V);
     return IntOp;
@@ -9313,12 +9365,12 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
   case ICmpInst::ICMP_NE: {                     // while (X != Y)
     // Convert to: while (X-Y != 0)
     if (LHS->getType()->isPointerTy()) {
-      LHS = getLosslessPtrToIntExpr(LHS);
+      LHS = getLosslessPtrToAddrExpr(LHS);
       if (isa<SCEVCouldNotCompute>(LHS))
         return LHS;
     }
     if (RHS->getType()->isPointerTy()) {
-      RHS = getLosslessPtrToIntExpr(RHS);
+      RHS = getLosslessPtrToAddrExpr(RHS);
       if (isa<SCEVCouldNotCompute>(RHS))
         return RHS;
     }
@@ -9331,12 +9383,12 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
   case ICmpInst::ICMP_EQ: {                     // while (X == Y)
     // Convert to: while (X-Y == 0)
     if (LHS->getType()->isPointerTy()) {
-      LHS = getLosslessPtrToIntExpr(LHS);
+      LHS = getLosslessPtrToAddrExpr(LHS);
       if (isa<SCEVCouldNotCompute>(LHS))
         return LHS;
     }
     if (RHS->getType()->isPointerTy()) {
-      RHS = getLosslessPtrToIntExpr(RHS);
+      RHS = getLosslessPtrToAddrExpr(RHS);
       if (isa<SCEVCouldNotCompute>(RHS))
         return RHS;
     }
@@ -9926,6 +9978,13 @@ static Constant *BuildConstantFromSCEV(const SCEV *V) {
     return cast<SCEVConstant>(V)->getValue();
   case scUnknown:
     return dyn_cast<Constant>(cast<SCEVUnknown>(V)->getValue());
+  case scPtrToAddr: {
+    const SCEVPtrToAddrExpr *P2I = cast<SCEVPtrToAddrExpr>(V);
+    if (Constant *CastOp = BuildConstantFromSCEV(P2I->getOperand()))
+      return ConstantExpr::getPtrToAddr(CastOp, P2I->getType());
+
+    return nullptr;
+  }
   case scPtrToInt: {
     const SCEVPtrToIntExpr *P2I = cast<SCEVPtrToIntExpr>(V);
     if (Constant *CastOp = BuildConstantFromSCEV(P2I->getOperand()))
@@ -9984,6 +10043,7 @@ ScalarEvolution::getWithOperands(const SCEV *S,
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
     return getCastExpr(S->getSCEVType(), NewOps[0], S->getType());
   case scAddRecExpr: {
@@ -10068,6 +10128,7 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) {
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
@@ -13204,12 +13265,12 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
   const SCEV *OrigStart = Start;
   const SCEV *OrigRHS = RHS;
   if (Start->getType()->isPointerTy()) {
-    Start = getLosslessPtrToIntExpr(Start);
+    Start = getLosslessPtrToAddrExpr(Start);
     if (isa<SCEVCouldNotCompute>(Start))
       return Start;
   }
   if (RHS->getType()->isPointerTy()) {
-    RHS = getLosslessPtrToIntExpr(RHS);
+    RHS = getLosslessPtrToAddrExpr(RHS);
     if (isa<SCEVCouldNotCompute>(RHS))
       return RHS;
   }
@@ -13512,12 +13573,12 @@ ScalarEvolution::ExitLimit ScalarEvolution::howManyGreaterThans(
   }
 
   if (Start->getType()->isPointerTy()) {
-    Start = getLosslessPtrToIntExpr(Start);
+    Start = getLosslessPtrToAddrExpr(Start);
     if (isa<SCEVCouldNotCompute>(Start))
       return Start;
   }
   if (End->getType()->isPointerTy()) {
-    End = getLosslessPtrToIntExpr(End);
+    End = getLosslessPtrToAddrExpr(End);
     if (isa<SCEVCouldNotCompute>(End))
       return End;
   }
@@ -14148,6 +14209,7 @@ ScalarEvolution::computeLoopDisposition(const SCEV *S, const Loop *L) {
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
@@ -14229,6 +14291,7 @@ ScalarEvolution::computeBlockDisposition(const SCEV *S, const BasicBlock *BB) {
   case scTruncate:
   case scZeroExtend:
   case scSignExtend:
+  case scPtrToAddr:
   case scPtrToInt:
   case scAddExpr:
   case scMulExpr:
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index a1751c0ee3e48..aac4a96704162 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -2894,6 +2894...
[truncated]

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to have this support, but I don't think we should use it for trip count calculation at this point, and only for explicit ptrtoaddr in IR. We have zero optimization support for this instruction currently, so I don't think we should be emitting it yet.

@fhahn
Copy link
Contributor Author

fhahn commented Sep 11, 2025

Sure, let me split things up

@nikic
Copy link
Contributor

nikic commented Oct 14, 2025

Ping on splitting this.

fhahn added a commit that referenced this pull request Oct 14, 2025
Add tests with ptrtoaddr instructions for
#158032.

Based on llvm/test/Analysis/ScalarEvolution/ptrtoint.ll.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Oct 14, 2025
Add tests with ptrtoaddr instructions for
llvm/llvm-project#158032.

Based on llvm/test/Analysis/ScalarEvolution/ptrtoint.ll.
@fhahn fhahn changed the title [SCEV] Add SCEVPtrToAddr, use when computing backedge taken counts. [SCEV] Add initial support for ptrtoaddr. Oct 14, 2025
@fhahn
Copy link
Contributor Author

fhahn commented Oct 14, 2025

The latest version should just have the bits needed to support ptrtoaddr LLVM IR instructions for now

@fhahn
Copy link
Contributor Author

fhahn commented Oct 14, 2025

(Still need to update polly...)

akadutta pushed a commit to akadutta/llvm-project that referenced this pull request Oct 14, 2025
Add tests with ptrtoaddr instructions for
llvm#158032.

Based on llvm/test/Analysis/ScalarEvolution/ptrtoint.ll.
@@ -1,2 +1,2 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
; RUN: opt < %s --data-layout="e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -S -disable-output -disable-verify "-passes=print<scalar-evolution>" 2>&1 | FileCheck --check-prefixes=ALL,X64 %s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't test -disable-verify here. Unlike ptrtoint, the ptrtoaddr result type is fixed (and equal to the "effective SCEV type" of the pointer), so any zext/trunc handling is not applicable for this instruction. getPtrToAddrExpr() should probably not even accept a type argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I removed the pointer argument and asserted that the conversion is always lossless, thanks

@fhahn fhahn force-pushed the scev-ptrtoaddr branch 2 times, most recently from 4b0d79b to fc7a601 Compare October 24, 2025 10:29
; RUN: opt -passes='print<scalar-evolution>' -disable-output %s 2>&1 | FileCheck %s

declare void @useptr(ptr)
target datalayout="e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please also add a test for a 64:64:64:32 pointer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I added it as a seperate test file, thanks

; X32-NEXT: --> (trunc i32 (ptrtoaddr ptr %in to i32) to i16) U: full-set S: full-set
; X32-NEXT: %p3 = ptrtoaddr ptr %in to i128
; X32-NEXT: --> %p3 U: full-set S: full-set
; X32-NEXT: --> (zext i32 (ptrtoaddr ptr %in to i32) to i128) U: [0,4294967296) S: [0,4294967296)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover check lines.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped, thanks

; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
; RUN: opt -passes='print<scalar-evolution>' -disable-output %s 2>&1 | FileCheck %s

target datalayout="p64:64:64:32"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
target datalayout="p64:64:64:32"
target datalayout="p:64:64:64:32"

And I think then it's going to fail because it's not considered "lossless".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh, yep that should be fixed in the latest version, thanks. replaced the assertion with a bail out

Add support for PtrToAddr to SCEV and use it to compute pointer difference when computing backedge taken counts.

This patch retains SCEVPtrToInt, which is mostly used to expressions comping directly from IR.

PtrToAddr more closely matches the semantics of most uses in SCEV. See llvm#156978 for some related discussion.

The patch still retains SCEVPtrToInt, as it is used for cases where a SCEV expression is used in IR by a ptrtoint, e.g. https://github.com/llvm/llvm-project/blob/main/llvm/test/Transforms/IndVarSimplify/pr59633.ll. Some (or perhaps all, still need to check) should probably just be SCEVUnknown.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:PowerPC llvm:analysis Includes value tracking, cost tables and constant folding llvm:ir llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants